export class PausableTimeout {
  private callback: () => void;
  private delay: number;
  private startTime: number | null = null;
  private remaining: number;
  private timerId: number | null = null;
  private isPaused: boolean = false;

  constructor(callback: () => void, delay: number) {
    this.callback = callback;
    this.delay = delay;
    this.remaining = delay;
  }

  private run(timestamp: number): void {
    if (this.startTime === null) {
      this.startTime = timestamp;
    }

    const elapsed = timestamp - this.startTime;

    if (elapsed < this.remaining) {
      this.timerId = requestAnimationFrame(this.run.bind(this));
    } else {
      this.callback();
      this.timerId = null;
    }
  }

  public start(): this {
    if (this.timerId === null && !this.isPaused) {
      this.timerId = requestAnimationFrame(this.run.bind(this));
    }
    return this;
  }

  public pause(): this {
    if (this.timerId !== null) {
      cancelAnimationFrame(this.timerId);
      this.remaining -= performance.now() - (this.startTime || 0);
      this.timerId = null;
      this.startTime = null;
      this.isPaused = true;
    }
    return this;
  }

  public resume(): this {
    if (this.isPaused) {
      this.isPaused = false;
      this.start();
    }
    return this;
  }

  public reset(): this {
    this.pause();
    this.remaining = this.delay;
    this.isPaused = false;
    return this;
  }

  public cancel(): this {
    if (this.timerId !== null) {
      cancelAnimationFrame(this.timerId);
    }
    this.timerId = null;
    this.startTime = null;
    this.remaining = this.delay;
    this.isPaused = false;
    return this;
  }
}